/**
 * Implementacion de la clase graph
 */

#include "graph.hpp"
#include <stdlib.h>
#include <algorithm>
#include <cctype>

	Graph::Graph(){
		this->numberOfNodes = 0;
		this->numberOfEdges = 0;
	}
    
	int Graph::getNumberOfNodes(){
		return this->numberOfNodes;
	}

	int Graph::getNumberOfEdges(){
		return this->numberOfEdges;
	}


    List<Node>* Graph::getNodes(){
		return &(this->nodes);
	}


	List<Edge>* Graph::getEdges(){
		return &(this->edges);
	}

	Node* Graph::getNode(std::string id){
        if (this->numberOfNodes == 0)
            return NULL;
        Node *node = new Node(id,0,0);
        Node *nodeTemp = this->nodes.search(node);
        delete node;
        return nodeTemp;

    }

    Edge* Graph::getEdge(std::string edgeId, std::string nodeId)
    {
    	Node *nodeTemp = this->getNode(nodeId);
    	if(nodeTemp != NULL)
    	{
    		List<Edge> *edgeL = nodeTemp->getEdgeList();
    		Edge e;
    		e.setId(edgeId);
    		return edgeL->search(&e);
    	}
    	else
    		return NULL;
    }


	Edge* Graph::getEdge(std::string roadName, int height)
    {
		// Para cada arista 
		ListIterator<Edge> it =  this->edges.iterator();
		
		//Voy a comparar en mayusculas
		std::transform (roadName.begin(), roadName.end(), roadName.begin(),std::toupper); 
		

		while(it.hasNext())
		{
			Edge *temp = it.next();
			
			std::string tempRoadName = temp->getRoadName();
			// Comparo en mayusculas
			std::transform (tempRoadName.begin(), tempRoadName.end(), tempRoadName.begin(),std::toupper); 
			if(tempRoadName == roadName)
			{
				// Chequeo la altura hacia los dos lados por las dudas
				if(((temp->getInitialHeight() <= height && temp->getFinalHeight() >= height) || (temp->getInitialHeight() >= height && temp->getFinalHeight() <= height)) && ((temp->getRoadSide() == AMBOS) ||(temp->getRoadSide()==IMPAR && height%2>0) || (temp->getRoadSide()==PAR && height%2==0) ) )
				{
					return temp;
				}
			}
		}

		return NULL;
    }


	Edge* Graph::getEdge(std::string roadName, Node *nodeOrigin)
    {

		// Para cada arista 
		ListIterator<Edge> it =  nodeOrigin->getEdgeList()->iterator();
		
		//Voy a comparar en mayusculas
		std::transform (roadName.begin(), roadName.end(), roadName.begin(),std::toupper); 
		

		while(it.hasNext())
		{
			Edge *temp = it.next();
			
			std::string tempRoadName = temp->getRoadName();
			// Comparo en mayusculas
			std::transform (tempRoadName.begin(), tempRoadName.end(), tempRoadName.begin(),std::toupper); 
			if(tempRoadName == roadName)
			{
				return temp;
			}
		}

		return NULL;
    }

	Edge* Graph::getEdge(std::string roadName, Node *nodeOrigin , Node *nodeDestiny)
    {

		// Para cada arista 
		ListIterator<Edge> it =  nodeOrigin->getEdgeList()->iterator();
		
		//Voy a comparar en mayusculas
		std::transform (roadName.begin(), roadName.end(), roadName.begin(),std::toupper); 
		

		while(it.hasNext())
		{
			Edge *temp = it.next();
			
			std::string tempRoadName = temp->getRoadName();
			// Comparo en mayusculas
			std::transform (tempRoadName.begin(), tempRoadName.end(), tempRoadName.begin(),std::toupper); 
			if(tempRoadName == roadName && temp->getDestinyId()==nodeDestiny->getId())
			{
				return temp;
			}
		}

		return NULL;
    }


	void Graph::addNode(Node *node){
        ++this->numberOfNodes;
        this->nodes.add(node);
	}

	int Graph::addEdge(Edge *edge){
        Node nodeOrigin(edge->getOriginId(),0,0);
        Node *nodeTemp = this->nodes.search(&nodeOrigin);

        if(nodeTemp){
        	Edge *edgeTemp = nodeTemp->getEdgeList()->search(edge);
        	if(!edgeTemp){
				this->edges.add(edge);
        		nodeTemp->addEgde(edge);

				//Seteo el Side de la calle
				this->setRoadSide(edge);								

            	nodeTemp = NULL;

    			return OK;
        	}
        }
 
		nodeTemp = NULL;

		return ERROR;
	}

	int Graph::modifyNode(Node node, std::string idNode){
		Node *nodeTemp = this->nodes.search(&node);
		if(nodeTemp){
			nodeTemp->setPositionX(node.getPositionX());
			nodeTemp->setPositionY(node.getPositionY());
			nodeTemp->setTemporalWeight(node.getTemporalWeight());
			nodeTemp->setMinimunPathEdge(node.getMinimunPathEdge());
			//nodeTemp->setEdgeList(node.getEdgeList());
			nodeTemp = NULL;
			return OK;
		}

		return ERROR;
	}

	int Graph::modifyEdge(Edge edge, std::string idEgde){
		Node nodeOrigin(edge.getOriginId(),0,0);
		Node *nodeTemp = this->nodes.search(&nodeOrigin);
		if(nodeTemp){
			Edge *edgeTemp = nodeTemp->getEdgeList()->search(&edge);
			if(edgeTemp){
				edgeTemp->setDestiny(edge.getDestinyId());
				edgeTemp->setWeight(edge.getWeight());
				edgeTemp = NULL;
				nodeTemp = NULL;
				return OK;
			}
		}

		return ERROR;
	}

	// TODO Importante: cuando se definan los iteradores
	// hacer la verificacion de las aristas que inciden
	// sobre el nodo borrado.
	int Graph::deleteNode(std::string id){
		Node n(id,0,0);
		Node *nodeTemp = this->nodes.remove(&n);
		if(nodeTemp){
			delete nodeTemp;
			return OK;
		}

		return ERROR;
	}

	int Graph::deleteEdge(std::string nodeId, std::string edgeId){
		Node n(nodeId,0,0);
		Node *nodeTemp = this->nodes.search(&n);
		if(nodeTemp){
			Weight w(0,0);
			Edge e(edgeId,"","",w);
			Edge *edgeTemp0 = this->edges.remove(&e);
			Edge *edgeTemp = nodeTemp->getEdgeList()->remove(&e);
			if(edgeTemp){
				delete edgeTemp;
				return OK;
			}
		}

		return ERROR;
	}
	
	void Graph::cleanBestPathValues(){

		ListIterator<Node> nit = this->getNodes()->iterator();
		while(nit.hasNext())
		{
			Node *node = nit.get();
			node->cleanMPInfo();
			nit.next();
		}
	}

    Graph::~Graph(){

    	unsigned nSize = this->nodes.size();
    	unsigned i     = 0;
		Node **nodeVec = NULL;

		//initialise the array.
		nodeVec = ( Node** )malloc(( sizeof( Node* ) * nSize ));


		// TODO Modificacion temporal
		ListIterator<Node> nit = this->getNodes()->iterator();
		while(nit.hasNext()){
			nodeVec[i]     = nit.remove();

			unsigned eSize = nodeVec[i]->getEdgeList()->size();
			unsigned j     = 0;

			Edge **edgeVec = NULL;

			//initialise the array.
			edgeVec = ( Edge** )malloc(( sizeof( Edge* ) * eSize ));

			// TODO Modificacion temporal
			ListIterator<Edge> eit = nodeVec[i]->getEdgeList()->iterator();
			while(eit.hasNext()){
				edgeVec[j] = eit.remove();
				Edge *tempEdge = this->edges.remove(edgeVec[j]);
				j++;
			}

			for(j = 0; j < eSize; j++){
				delete edgeVec[j];
			}

			free( edgeVec );
			edgeVec = NULL;

			i++;
		}

		for(i = 0; i < nSize; i++){
			delete nodeVec[i];
		}

		free( nodeVec );
		nodeVec = NULL;
	} 



	bool Graph::checkSolapation(std::string edgeRoadName,int edgeInitialHeight,int edgeFinalHeight,std::string originNodeId, std::string destinyNodeId)
	{
		// Para cada arista 
		ListIterator<Edge> it =  this->edges.iterator();
		
		//Voy a comparar en mayusculas
		std::transform (edgeRoadName.begin(), edgeRoadName.end(), edgeRoadName.begin(),std::toupper); 
		

		while(it.hasNext())
		{
			Edge *temp = it.next();
			
			std::string tempRoadName = temp->getRoadName();
			// Comparo en mayusculas
			std::transform (tempRoadName.begin(), tempRoadName.end(), tempRoadName.begin(),std::toupper); 
			if(tempRoadName == edgeRoadName)
			{
				//Me fijo si hay solapamiento alguno de inicio, de fin, o de contencion
				if((temp->getInitialHeight() <= edgeInitialHeight &&  temp->getFinalHeight() >= edgeInitialHeight) || (temp->getInitialHeight() <= edgeFinalHeight &&  temp->getFinalHeight() >= edgeFinalHeight) || (temp->getInitialHeight() >= edgeInitialHeight &&  temp->getFinalHeight() <= edgeFinalHeight) || (temp->getInitialHeight() >= edgeFinalHeight &&  temp->getFinalHeight() <= edgeInitialHeight))
				{
					if(!((temp->getInitialHeight()== edgeFinalHeight && temp->getFinalHeight()== edgeInitialHeight) && (temp->getOriginId()== destinyNodeId  && temp->getDestinyId() == originNodeId )))
					{
						return true;
					}
				}
			}
		}

		return false;

	}

	// Private

	void Graph::setRoadSide(Edge *edge)
	{
		
        Node nodeOrigin(edge->getOriginId(),0,0);
        Node *nodeOTemp = this->nodes.search(&nodeOrigin);

		Node nodeDestiny(edge->getDestinyId(),0,0);
        Node *nodeDTemp = this->nodes.search(&nodeDestiny);

		if(nodeOTemp && nodeDTemp)
		{
			Edge *tempEdge = this->getEdge(edge->getRoadName(),nodeDTemp,nodeOTemp);

			if(tempEdge)
			{
				//Si existe una calle con el mismo nombre que tega por origen nuestro nodo destiny
				if(edge->getInitialHeight() > edge->getFinalHeight())
				{
					//Si el sentido de la calle va hacia donde baja la altura supongo circulacion derecha entonces la derecha sera par
					edge->setRoadSide(PAR);

					//La calle en el otro sentido sera impar
					tempEdge->setRoadSide(IMPAR);
				}
				else
				{
					//viceversa
					edge->setRoadSide(IMPAR);
					tempEdge->setRoadSide(PAR);
				}
			}
			else
			{
				edge->setRoadSide(AMBOS);
			}

		}
	}
